package chapter16Condition;

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * Created by LinGer on 2019/6/24
 */
public class BoundedBuffer {

    private final Lock lock = new ReentrantLock();
    private final Condition notFull = lock.newCondition();
    private final Condition notEmpty = lock.newCondition();

    /**
     * 缓冲区
     */
    private final Object[] items = new Object[5];

    /**
     * 写索引
     */
    private int putptr;

    /**
     * 读索引
     */
    private int takeptr;

    /**
     * 队列中存在的数据个数
     */
    private int count;

    public void put(Object x){
        lock.lock();
        try {
            //如果缓冲区已经满了，则等待；否则将x添加到缓冲区中
            while (count == items.length){
                System.out.println("缓冲区满");
                notFull.await();
            }

            items[putptr++] = x;

            //若写索引写到队列的最后一个位置了，则重置putptr
            if(putptr == items.length){
                putptr = 0;
            }

            count++;

            //一旦插入就唤醒读线程阻塞队列中其中一个线程
            notEmpty.signal();

            System.out.println(Thread.currentThread().getName() + " put: " + x);

        } catch (InterruptedException e) {
            e.printStackTrace();
        }finally {
            lock.unlock();
        }
    }

    public Object take(){
        lock.lock();
        Object x = new Object();
        try {
            while (count == 0){
                System.out.println("缓冲区空");
                notEmpty.await();
            }

            x = items[takeptr++];

            //若读索引写到队列的最后一个位置了，重置takeptr
            if(takeptr == items.length){
                takeptr = 0;
            }

            count--;

            //一旦读取就唤醒写线程阻塞队列中的一个
            notFull.signal();

            System.out.println(Thread.currentThread().getName() + " take: " + x);

        } catch (InterruptedException e) {
            e.printStackTrace();
        }finally {
            lock.unlock();
        }

        return x;
    }
}
